package client;
import common.*;

import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Enumeration;

/**
 * Classe finalizzata alla comunicazione con il server tramite protocollo RMI. La classe istanzia se stessa sulla macchina fisica su cui viene eseguita cosi' da poter essere scaricata la sua interfaccia dal server (a cui sara' passato l'ip della macchina).<br \>
 * In seguito, scarichera' l'interfaccia del server su un indirizzo predefinito e sul cui oggetto saranno chiamati i metodi interessati.
 */
public class Client_RMI extends UnicastRemoteObject implements Client, ClientInterface_RMI{

	private static final long serialVersionUID = 1l;
	private C_GSessioneGioco cg;
	private ServerInterface_RMI server;
	private ArrayList<Object> util = new ArrayList<Object>();
	private String[][] mappa;
	private String[] lista;
	@SuppressWarnings("unused")
	private Registry registro;
	private String ip;
	private static final String port = "1099"; //porta su cui avviene il collegamento client-server
	
	/**
	 * Costruttore della classe: carica sulla macchina fisica su cui viene fatta girare la classe stessa, in modo da poter essere scaricata dal server.
	 * @throws RemoteException Eccezione lanciata nel caso si verifichino errori nel caricare la propria istanza sull'indirizzo predefinito.
	 */
	public Client_RMI() throws RemoteException{
		
		this.cg = new C_GSessioneGioco(this);
		try {
			try{
			registro = LocateRegistry.createRegistry(Integer.parseInt(port));
			} catch(Exception e){
				registro = LocateRegistry.getRegistry(Integer.parseInt(port));
			}
			Naming.bind("rmi://127.0.0.1/client:" + port, (Remote) this);
			Naming.rebind("rmi://127.0.0.1/client:" + port, (Remote) this); //caricamento dell'oggetto all'indirizzo locale
			System.out.println("Client RMI avviato !!!");
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (AlreadyBoundException e) {
			e.printStackTrace();
		}
		
		try //alogritmo utile per recuperare il proprio indirizzo ip
		   	{
		    for (Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces(); ifaces.hasMoreElements();) 
		    {
		    	NetworkInterface iface = ifaces.nextElement();
		        for (Enumeration<InetAddress> addresses = iface.getInetAddresses(); addresses.hasMoreElements();) 
		        {
		         InetAddress address = addresses.nextElement();
		         if (address instanceof Inet4Address) 
		         {
		          if(!address.getHostAddress().equals("127.0.0.1"))
		          {
		           ip = address.getHostAddress();
		          }
		         }
		        }
		     }
		    } 
		    catch (SocketException e) 
		    {
		     System.out.println("IP del server non trovato");
		    }
		    this.run();
	}
	
	/**
	 * Metodo chiamato per scaricare l'interfaccia del server sul cui oggetto saranno fatte le richieste.
	 */
	private void run(){
		try {
			this.server = (ServerInterface_RMI) Naming.lookup("rmi://10.42.43.1/server:" + port); //lookup eseguita sull'ip predefinito e sulla porta interessata.
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (RemoteException e) {
			noConnection();
		} catch (NotBoundException e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public void creaUtente(String usr, String pass){
		try {
			util = server.creaUtente(usr, pass);
		} catch (RemoteException e) {
			noConnection();
		}
		Boolean par = (Boolean)util.get(0);
		System.out.println("Chiamo creaUtente !!");
		if( par == false ) //caso errore
			cg.errore(Integer.parseInt(String.valueOf(util.get(1))));
		else cg.creaUtente(); //caso avvenuta creazione utente
	}

	/**
	 * Metodo usato per richiedere al server un login. Controllo di conferma/errore tramite il controllo della prima cella dell'array ritornato.<br \>
	 * In caso di avvenuto login, il client chiama un metodo sull'interfaccia del server passandogli il proprio indirizzo ip, permettendo cosi' al server il download dell'interfaccia del client.
	 * @param usr Parametro indicante il nomeUtente con cui l'utente si e' precedentemente registato.
	 * @param pass Password precedentemente scelta dall'utente.
	 */
	public void login(String usr, String pass) {
		try {
			util = server.login(usr, pass);
		
		Boolean par = (Boolean)util.get(0);
		System.out.println("Chiamo login !!");
		if( par == false ) //caso errore
			cg.errore(Integer.parseInt(String.valueOf(util.get(1))));
		else{ //caso avvenuto login
			try {
				server.registrazione(ip, Integer.parseInt(String.valueOf(util.get(1)))); //registrazione del client passando il proprio ip, permettendo al server di avere una lista di client connessi.
				System.out.println("Server preso, registrazione avvenuta !!!");
			} catch (NumberFormatException e) {
				e.printStackTrace();
			} catch (RemoteException e) {
				e.printStackTrace();
			}
			cg.login(Integer.parseInt(String.valueOf(util.get(1))));
		}
		} catch (RemoteException e) {
			noConnection();
		}
	}

	@Override
	public void creaRazza(int tok, String nome, String ti) {
		String tipo;
		
		if( ti.equals("C") ) //pongo le lettere indicanti il tipo del dinosauro scelto minuscole
			tipo = "c";
		else
			tipo = "e";
		try {
			util = server.creaRazza(tok, nome, tipo);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		Boolean par = (Boolean) util.get(0);
		System.out.println("Chiamo creaRazza !!");
		if( par == false ) //caso errore
			cg.errore(Integer.parseInt(String.valueOf(util.get(1))));
		else{} //in caso di conferma, non viene chiamato nulla sull'interfaccia grafica
	}

	@Override
	public void accessoPartita(int tok) {
		
		try {
			util = server.accessoPartita(tok);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		Boolean par = (Boolean) util.get(0);
		System.out.println("Chiamo accessoPartita !!");
		if( par == false ) //caso errore
			cg.errore(Integer.parseInt(String.valueOf(util.get(1))));
		else{ //caso accesso partita
			cg.accessoPartita();
			if( (Boolean) util.get(1) == true) //caso primo giocatore
				try {
					server.cambioTurnoPartita();
				} catch (RemoteException e) {
					e.printStackTrace();
				}
		}
	}

	@Override
	public void uscitaPartita(int tok) {
		try {
			util = server.uscitaPartita(tok);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		Boolean par = (Boolean) util.get(0);
		System.out.println("Chiamo uscitaPartita !!");
		if( par == false ) //caso errore
			cg.errore(Integer.parseInt(String.valueOf(util.get(1))));
		else{ //caso uscita partita
			cg.uscitaPartita();
		}
	}

	@Override
	public void listaGiocatori(int tok) {
		try {
			util = server.listaGiocatori(tok);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		Boolean par = (Boolean) util.get(0);
		System.out.println("Chiamo listaGiocatori !!");
		if( par == false) //caso errore
			cg.errore(Integer.parseInt(String.valueOf(util.get(1))));
		else{ //caso ricevuta lista giocatori
			cg.listaGiocatori((String[])(util.get(1)));
		}
	}

	@Override
	public void classifica(int tok) {
		try {
			util = server.classifica(tok);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		Boolean par = (Boolean) util.get(0);
		System.out.println("Chiamo classifica !!");
		if( par == false ) //caso errore
			cg.errore(Integer.parseInt(String.valueOf(util.get(1))));
		else{ //caso classifica ricevuta
			util.remove(0);
			mappa = new String[util.size()][4];
			for( int i=0; i < util.size(); i++){ //per ogni utente in classifica, prelevo i suoi dati dall'array ritornatomi e pongo i dati estratti in una matrice che passero' poi all'interfaccia grafica
				mappa[i][0] = ((String[]) (util.get(i)))[0];
				mappa[i][1] = ((String[]) (util.get(i)))[1];
				mappa[i][2] = ((String[]) (util.get(i)))[2];
				mappa[i][3] = ((String[]) (util.get(i)))[3];				
			}
			cg.classifica(mappa);
		}
	}

	@Override
	public void logout(int tok) {
		try {
			util = server.logout(tok);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		Boolean par = (Boolean) util.get(0);
		System.out.println("Chiamo logout !!");
		if( par == false ) //caso errore
			cg.errore(Integer.parseInt(String.valueOf(util.get(1))));
		else{ //caso conferma logout
			cg.logout();
			try {
				Naming.unbind("rmi://127.0.0.1/client:" + port);
			} catch (RemoteException e) {
				e.printStackTrace();
			} catch (MalformedURLException e) {
				e.printStackTrace();
			} catch (NotBoundException e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	public void mappaGenerale(int tok) {
		try {
			util = server.mappaGenerale(tok);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		Boolean par = (Boolean) util.get(0);
		System.out.println("Chiamo mappaGenerale !!");
		if( par == false ) //caso errore
			cg.errore(Integer.parseInt(String.valueOf(util.get(1))));
		else{ //caso ricevuta mappaGenerale. Prendo i dati dall'array list e li pongo in delle variabili da passare all'interfaccia grafica
			int rig = (Integer) util.get(1);
			int col = (Integer) util.get(2);
			String[][] mappa = (String[][]) util.get(3);
			cg.mappaGenerale(rig, col, mappa);
		}
	}

	@Override
	public void listaDinosauri(int tok) {
		try {
			util = server.listaDinosauri(tok);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		Boolean par = (Boolean)util.get(0);
		System.out.println("Chiamo listaDinosauri !!");
		if( par == false ) //caso errore
			cg.errore(Integer.parseInt(String.valueOf(util.get(1))));
		else{ //caso ricevuta lista. Pongo i nomi dei dinosauri dentro un array
			util.remove(0);
			lista = (String[]) util.get(0);
			cg.listaDinosauri(lista);
		}
	}

	@Override
	public void vistaLocale(int tok, String id) {
		try {
			util = server.vistaLocale(tok, id);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		Boolean par = (Boolean) util.get(0);
		System.out.println("Chiamo vistaLocale !!");
		if( par == false ) //caso errore
			cg.errore(Integer.parseInt(String.valueOf(util.get(1))));
		else{ //caso ricevuta vista locale
			util.remove(0);
			int x = Integer.parseInt(String.valueOf(util.get(0)));
			int y = Integer.parseInt(String.valueOf(util.get(1)));
			int rig = Integer.parseInt(String.valueOf(util.get(2)));
			int col = Integer.parseInt(String.valueOf(util.get(3)));
			mappa = (String[][]) (util.get(4));
			cg.vistaLocale(x, y, rig, col, mappa);
		}
	}

	@Override
	public void statoDinosauro(int tok, String id) {
		try {
			util = server.statoDinosauro(tok, id);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		Boolean par = (Boolean) util.get(0);
		System.out.println("Chiamo statoDinosauro !!");
		if( par == false ) //caso errore
			cg.errore(Integer.parseInt(String.valueOf(util.get(1))));
		else{ //caso ricevute informazioni sullo stato dinosauro.
			util.remove(0);
			String userId="";
			String razza="";
			char tipo=' ';
			int posX=-1;
			int posY=-1;
			int dimensione=-1;
			int energia=-1;
			int vita = -1;
			for( int i=0; i < util.size(); i++){
				switch(i){
				case 0:
					userId = (String) util.get(i);
					break;
				case 1:
					razza = (String) util.get(i);
					break;
				case 2:
					tipo = (Character) util.get(i);
					break;
				case 3:
					posX = (Integer) util.get(i);
					break;
				case 4:
					posY = (Integer) util.get(i);
					break;
				case 5:
					dimensione = (Integer) util.get(i);
					break;
				case 6:
					energia = (Integer) util.get(i);
					break;
				case 7:
					vita = (Integer) util.get(i);
					break;
				}
			}
			cg.statoDinosauro(userId, razza, tipo, posX, posY, dimensione, energia, vita);
		}	
	}

	@Override
	public void muoviDinosauro(int tok, String id, int x, int y) {
		try {
			util = server.muoviDinosauro(tok, id, x, y);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		Boolean par = (Boolean)util.get(0);
		System.out.println("Chiamo muoviDinosauro !!");
		if( par == false ){ //caso errore
			if( Integer.parseInt(String.valueOf(util.get(1))) == 101 ){} //caso dinosauro morto e razza estinta
			cg.errore(Integer.parseInt(String.valueOf(util.get(1))));
		}
		else{ //caso conferma movimento
			if( (Boolean) util.get(1) == true ) //caso combattimento svolto
				if( (Integer) util.get(2) == 2 )
				cg.muoviDinosauro("p"); //caso sconfitta del proprio dinosauro nel combattimento
			cg.muoviDinosauro("");	
		}
	}
	
	@Override
	public void cresciDinosauro(int tok, String id) {
		try {
			util = server.cresciDinosauro(tok, id);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		Boolean par = (Boolean) util.get(0);
		System.out.println("Chiamo cresciDinosauro !!");
		if( par == false ){ //caso errore
			if( Integer.parseInt(String.valueOf(util.get(1))) == 101 ){} //caso dinosauro morto e razza estinta
			cg.errore(Integer.parseInt(String.valueOf(util.get(1))));
		}
		else{ //caso avvenuta crescita
			mappaGenerale(tok);	
		}
	}

	@Override
	public void deponiUovo(int tok, String id) {
		try {
			util = server.deponiUovo(tok, id);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		Boolean par = (Boolean) util.get(0);
		System.out.println("Chiamo deponiUovo !!");
		if( par == false ){ //caso errore
			if( Integer.parseInt(String.valueOf(util.get(1))) == 101 ){} //caso dinosauro morto e razza estinta
			cg.errore(Integer.parseInt(String.valueOf(util.get(1))));
		}
		else{ //caso uovo deposto
			mappaGenerale(tok);	
		}
	}

	@Override
	public void confermaTurno(int tok) {
		try{
			util = server.confermaTurno(tok);
		} catch (RemoteException e){
			e.printStackTrace();
		}
		Boolean par = (Boolean) util.get(0);
		System.out.println("Chiamo confermaTurno !!!");
		if ( par == false ) //caso errore
			cg.errore(Integer.parseInt(String.valueOf(util.get(1))));
	}

	@Override
	public void passaTurno(int tok) {
		try {
			util = server.passaTurno(tok);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		Boolean par = (Boolean) util.get(0);
		System.out.println("Chiamo passaTurno !!");
		if( par == false ) //caso errore
			cg.errore(Integer.parseInt(String.valueOf(util.get(1))));
		else //caso avvenuto passaggio turno
			try {
				server.cambioTurnoPartita();
			} catch (RemoteException e) {
				e.printStackTrace();
			}
	}
	
	@Override
	public void cambioTurno(String username){
		System.out.println("Chiamo cambioTurno !!");
		cg.cambioTurno(username);
	}
	
	@Override
	public void specieMorta(String username){
		System.out.println("Chiamo specieMorta !!");
		cg.specieMorta(username);
	}
	
	/**
	 * Metodo invocato nel caso la connessione con il server abbia riscontrato dei problemi.<br \>
	 * Il client viene rimandato alla finestra di avvio con relativa scelta connessione.
	 */
	private void noConnection(){
		/*try {
			System.out.println("Errore connessione: tentativo di connessione tra 15 secondi");
			cg.uscitaPartita();
			cg.logout();
			Thread.sleep(15000);
		} catch (InterruptedException e) {
			e.printStackTrace();	
		}
		this.run();*/
		cg.erroreConnessione();
		try {
			Naming.unbind("rmi://127.0.0.1/client:" + port);
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (NotBoundException e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public boolean exist(){
		return true;
	}
	
}
